#include "SEng.h"

void* SEng::cgpfnRenderFrame;

SEng::SEng(HINSTANCE hInstance, int nCmdShow, tRenderFrame pfnRenderFrame, int xres=800, int yres=600){
	WNDCLASSA WNDCLS = {
			 	0,
				WindowProcedure,
				0,
				0,
				hInstance,
				0,
				LoadCursor(NULL, IDC_ARROW),
				(HBRUSH)(COLOR_WINDOW+1),
				STRING_CLASSNAME,
				STRING_CLASSNAME
			};

	RegisterClassA(&WNDCLS);

	hWnd = CreateWindowA(STRING_CLASSNAME, STRING_WINDOWTITLE, WINDOW_STYLE, 0, 0, xres, yres, 0, 0, hInstance, 0);

	this->xres = xres;
	this->yres = yres;

	ShowWindow(hWnd, nCmdShow);

	this->pFrameBuffer = new tpixel[xres*yres];

	ZeroMemory(this->pFrameBuffer, (sizeof tpixel)*(xres*yres));
	ZeroMemory(&this->TerminalHandleList, sizeof(tHandleEntry));

	cgpfnRenderFrame = (void*)pfnRenderFrame;

	this->AddTerminalHandle(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&FrameTimer, hWnd, 0, 0));
	this->AddTerminalHandle(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&ForwardMessages, 0, 0, 0));
}

SEng::~SEng(){
	for(register tHandleEntry *hCache = &this->TerminalHandleList;hCache->pNextEntry;hCache = hCache->pNextEntry) CloseHandle(hCache->hValue);
}

LRESULT CALLBACK SEng::WindowProcedure(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam){
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		((tRenderFrame)SEng::cgpfnRenderFrame)(hdc, &ps);
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
		break;
	}
	return 0;
}

void SEng::FrameTimer(HWND hWnd){
	__int64 ticks=0, cache=0, multiplier=0;
	RECT rct = {0};
	GetClientRect(hWnd, &rct);
	QueryPerformanceCounter((LARGE_INTEGER*)&ticks);
	QueryPerformanceFrequency((LARGE_INTEGER*)&multiplier);
	while(1){
		QueryPerformanceCounter((LARGE_INTEGER*)&cache);
		if((double(cache)/double(multiplier))-(double(ticks)/double(multiplier))>(1/double(DEFAULT_FPS))){
			QueryPerformanceCounter((LARGE_INTEGER*)&ticks);
			//PushBuffer(self);
			InvalidateRect(hWnd, &rct, false);
			UpdateWindow(hWnd);
			//SendMessage(hWnd, WM_PAINT, 0, 0);
			//RedrawWindow(hWnd, &rct, 0, RDW_INVALIDATE);
		}
	}
}

void SEng::ForwardMessages(void){
	MSG msg;
	while(GetMessage(&msg, 0, 0, 0)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

void SEng::AddTerminalHandle(HANDLE hValue){
	register tHandleEntry *hCache;
	for(hCache = &this->TerminalHandleList;hCache->pNextEntry;hCache = hCache->pNextEntry);
	hCache->pNextEntry = new tHandleEntry;
	hCache = hCache->pNextEntry;
	hCache->pNextEntry = 0;
	hCache->hValue = hValue;
}

void SEng::PushBuffer(SEng *cEngine, HDC hdc){
	//void PBuffer(tpixel *pPixelData, unsigned long ulWidth, unsigned long ulHeight, HDC hdc){
	BITMAPINFO BMPINFO = {0};
	BMPINFO.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	BMPINFO.bmiHeader.biWidth = cEngine->xres;
	BMPINFO.bmiHeader.biHeight = cEngine->yres;
	BMPINFO.bmiHeader.biPlanes = 1;
	BMPINFO.bmiHeader.biBitCount = 32;
	BMPINFO.bmiHeader.biCompression = BI_RGB;
	BITMAPINFOHEADER BMPINFOHEAD = {0};
	BMPINFOHEAD.biHeight = cEngine->xres;
	BMPINFOHEAD.biWidth = cEngine->yres;
	BMPINFOHEAD.biCompression = BI_RGB;
	HDC hdcSource = CreateCompatibleDC(0);
	HBITMAP hBMP = CreateDIBitmap(hdc, (BITMAPINFOHEADER*)&BMPINFO, CBM_INIT, (void *)cEngine->GetFrameBuffer(), &BMPINFO, DIB_RGB_COLORS);
	SelectObject(hdcSource, hBMP);
	BitBlt(hdc,0,0,cEngine->xres, cEngine->yres, hdcSource,0,0,SRCCOPY); // depending on winapi version, add '| 0x40000000', usually for newer versions.
	DeleteObject(hBMP);
}